Passed
Push — master ( a28e32...efdf15 )
by Rafael S.
02:01
created

T_CONST ➔ unpackStruct   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
c 0
b 0
f 0
nc 3
dl 0
loc 1
rs 10
nop 3
1
/*
2
 * byte-data
3
 * Pack and unpack binary data.
4
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
5
 * https://github.com/rochars/byte-data
6
 *
7
 */
8
9
/** @private */
10
const endianness = require("endianness");
11
/** @private */
12
const GenericInteger = require("generic-integer");
13
14
/** @private */
15
const int8_ = new Int8Array(8);
16
/** @private */
17
const ui32_ = new Uint32Array(int8_.buffer);
18
/** @private */
19
const f32_ = new Float32Array(int8_.buffer);
20
/** @private */
21
const f64_ = new Float64Array(int8_.buffer);
22
23
/**
24
 * @type {Function}
25
 * @private
26
 */
27
let reader_;
28
/**
29
 * @type {Function}
30
 * @private
31
 */
32
let writer_;
33
/**
34
 * @type {Object}
35
 * @private
36
 */
37
let gInt_ = {};
38
39
/**
40
 * Write a number or fixed-length string to a byte buffer.
41
 * @param {number|string} value The value.
42
 * @param {!Object} theType The type definition.
43
 * @param {!number} base The base of the output. Optional. Default is 10.
44
 *      Possible values are 2, 10, 16.
45
 * @return {!Array<!number|!string>}
46
 * @throws {Error} If the type definition is not valid.
47
 */
48
function pack(value, theType, base=10) {
49
    setUp_(theType);
50
    let packed = [];
51
    if (value === undefined) {
52
        return packed;
53
    }
54
    value = fixBadString_(value, theType);
55
    return toBytes_([value], theType, base);
56
}
57
58
/**
59
 * Read a number or a fixed-length string from a byte buffer.
60
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
61
 * @param {!Object} theType The type definition.
62
 * @param {!number} base The base of the input. Optional. Default is 10.
63
 *      Possible values are 2, 10, 16.
64
 * @return {number|string|null}
65
 * @throws {Error} If the type definition is not valid.
66
 */
67
function unpack(buffer, theType, base=10) {
68
    setUp_(theType);
69
    let values = fromBytes_(buffer.slice(0, theType["offset"]), theType, base);
70
    return values ? values[0] : theType["char"] ? "" : null;
71
}
72
73
/**
74
 * Write an array of numbers or a string to a byte buffer.
75
 * @param {!Array<number|string>} values The values.
76
 * @param {!Object} theType The type definition.
77
 * @param {!number} base The base of the output. Optional. Default is 10.
78
 *      Possible values are 2, 10, 16.
79
 * @return {!Array<!number|!string>}
80
 * @throws {Error} If the type definition is not valid.
81
 */
82
function packArray(values, theType, base=10) {
83
    setUp_(theType);
84
    // Fix strings with bad length in the array
85
    if (theType["char"]) {
86
        let len = values.length;
87
        for (let i=0; i<len; i++) {
88
            values[i] = fixBadString_(values[i], theType);
89
        }
90
    }
91
    return toBytes_(values, theType, base);
92
}
93
94
/**
95
 * Read an array of numbers or a string from a byte buffer.
96
 * @param {!Array<number|string>|!Uint8Array} buffer The byte array.
97
 * @param {!Object} theType The type definition.
98
 * @param {!number} base The base of the input. Optional. Default is 10.
99
 *      Possible values are 2, 10, 16.
100
 * @return {!Array<number|string>|number}
101
 * @throws {Error} If the type definition is not valid.
102
 */
103
function unpackArray(buffer, theType, base=10) {
104
    setUp_(theType);
105
    return fromBytes_(buffer, theType, base);
106
}
107
108
/**
109
 * Turn a byte buffer into what the bytes represent.
110
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
111
 * @param {!Object} theType The type definition.
112
 * @param {!number} base The desired base of the ouput.
113
 * @return {!Array<number>}
114
 * @private
115
 */
116
function fromBytes_(buffer, theType, base) {
117
    // turn to BE if BE
118
    if (theType["be"]) {
119
        endianness(buffer, theType["offset"]);
120
    }
121
    let len = buffer.length;
122
    // turn the input to base 10 in case it is not
123
    if (base != 10) {
124
        for(let i=0; i < len; i++) {
125
            buffer[i] = parseInt(buffer[i], base);
126
        }
127
    }
128
    // unpack the values
129
    let values = [];
130
    len = len - (theType["offset"] - 1);
131
    for (let i=0; i<len; i+=theType["offset"]) {
132
        values.push(reader_(buffer, i));
133
    }
134
    return values;
135
}
136
137
/**
138
 * Turn numbers and strings to bytes.
139
 * @param {!Array<!number|!string>} values The data.
140
 * @param {!Object} theType The type definition.
141
 * @param {!number} base The desired base of the ouput.
142
 * @return {!Array<!number|!string>} the data as a byte buffer.
143
 * @private
144
 */
145
function toBytes_(values, theType, base) {
146
    let j = 0;
147
    let bytes = [];
148
    let len = values.length;
149
    // pack the values
150
    for(let i=0; i < len; i++) {
151
        j = writer_(bytes, values[i], j);
152
    }
153
    // turn to BE if BE
154
    if (theType["be"]) {
155
        endianness(bytes, theType["offset"]);
156
    }
157
    // turn the output to the desired base
158
    if (base != 10) {
159
        let offset = (base == 2 ? 8 : 2) + 1;
160
        len = bytes.length;
161
        for(let i=0; i < len; i++) {
162
            bytes[i] = bytes[i].toString(base);
163
            bytes[i] = Array(offset - bytes[i].length).join("0") + bytes[i];
164
        }
165
    }
166
    return bytes;
167
}
168
169
/**
170
 * Read int values from bytes.
171
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
172
 * @param {!number} i The index to read.
173
 * @return {!number}
174
 * @private
175
 */
176
function readInt_(bytes, i) {
177
    return gInt_.read(bytes, i);
178
}
179
180
/**
181
 * Read 1 16-bit float from bytes.
182
 * Thanks https://stackoverflow.com/a/8796597
183
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
184
 * @param {!number} i The index to read.
185
 * @return {!number}
186
 * @private
187
 */
188
function read16F_(bytes, i) {
189
    let int = gInt_.read(bytes, i);
190
    let exponent = (int & 0x7C00) >> 10;
191
    let fraction = int & 0x03FF;
192
    let floatValue;
193
    if (exponent) {
194
        floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
195
    } else {
196
        floatValue = 6.103515625e-5 * (fraction / 0x400);
197
    }
198
    return floatValue * (int >> 15 ? -1 : 1);
199
}
200
201
/**
202
 * Read 1 32-bit float from bytes.
203
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
204
 * @param {!number} i The index to read.
205
 * @return {!number}
206
 * @private
207
 */
208
function read32F_(bytes, i) {
209
    ui32_[0] = gInt_.read(bytes, i);
210
    return f32_[0];
211
}
212
213
/**
214
 * Read 1 64-bit float from bytes.
215
 * Thanks https://gist.github.com/kg/2192799
216
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
217
 * @param {!number} i The index to read.
218
 * @return {!number}
219
 * @private
220
 */
221
function read64F_(bytes, i) {
222
    ui32_[0] = gInt_.read(bytes, i);
223
    ui32_[1] = gInt_.read(bytes, i + 4);
224
    return f64_[0];
225
}
226
227
/**
228
 * Read 1 char from bytes.
229
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
230
 * @param {!number} i The index to read.
231
 * @return {!string}
232
 * @private
233
 */
234
function readChar_(bytes, i) {
235
    let chrs = "";
236
    for(let j=0; j < gInt_.offset; j++) {
237
        chrs += String.fromCharCode(bytes[i+j]);
238
    }
239
    return chrs;
240
}
241
242
/**
243
 * Write a integer value to a byte buffer.
244
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
245
 * @param {!number} number The number to write as bytes.
246
 * @param {!number} j The index being written in the byte buffer.
247
 * @return {!number} The next index to write on the byte buffer.
248
 * @private
249
 */
250
function writeInt_(bytes, number, j) {
251
    return gInt_.write(bytes, number, j);
252
}
253
254
/**
255
 * Write one 16-bit float as a binary value.
256
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
257
 * @param {!number} number The number to write as bytes.
258
 * @param {!number} j The index being written in the byte buffer.
259
 * @return {!number} The next index to write on the byte buffer.
260
 * @private
261
 */
262
function write16F_(bytes, number, j) {
263
    f32_[0] = number;
264
    let x = ui32_[0];
265
    let bits = (x >> 16) & 0x8000;
266
    let m = (x >> 12) & 0x07ff;
267
    let e = (x >> 23) & 0xff;
268
    if (e >= 103) {
269
        bits |= ((e - 112) << 10) | (m >> 1);
270
        bits += m & 1;
271
    }
272
    bytes[j++] = bits & 0xFF;
273
    bytes[j++] = bits >>> 8 & 0xFF;
274
    return j;
275
}
276
277
/**
278
 * Write one 32-bit float as a binary value.
279
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
280
 * @param {!number} number The number to write as bytes.
281
 * @param {!number} j The index being written in the byte buffer.
282
 * @return {!number} The next index to write on the byte buffer.
283
 * @private
284
 */
285
function write32F_(bytes, number, j) {
286
    f32_[0] = number;
287
    return gInt_.write(bytes, ui32_[0], j);
288
}
289
290
/**
291
 * Write one 64-bit float as a binary value.
292
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
293
 * @param {!number} number The number to write as bytes.
294
 * @param {!number} j The index being written in the byte buffer.
295
 * @return {!number} The next index to write on the byte buffer.
296
 * @private
297
 */
298
function write64F_(bytes, number, j) {
299
    f64_[0] = number;
300
    j = gInt_.write(bytes, ui32_[0], j);
301
    return gInt_.write(bytes, ui32_[1], j);
302
}
303
304
/**
305
 * Write one char as a byte.
306
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
307
 * @param {!string} str The string to write as bytes.
308
 * @param {!number} j The index being written in the byte buffer.
309
 * @return {!number} The next index to write on the byte buffer.
310
 * @private
311
 */
312
function writeChar_(bytes, str, j) {
313
    for (let i=0; i<str.length; i++) {
314
        bytes[j++] = str.charCodeAt(i);
315
    }
316
    return j;
317
}
318
319
/**
320
 * Set the function to unpack the data.
321
 * @param {!Object} theType The type definition.
322
 * @private
323
 */
324
function setReader_(theType) {
325
    if (theType["float"]) {
326
        if (theType["bits"] == 16) {
327
            reader_ = read16F_;
328
        } else if(theType["bits"] == 32) {
329
            reader_ = read32F_;
330
        } else if(theType["bits"] == 64) {
331
            reader_ = read64F_;
332
        }
333
    } else if (theType["char"]) {
334
        reader_ = readChar_;
335
    } else {
336
        reader_ = readInt_;
337
    }
338
}
339
340
/**
341
 * Set the function to pack the data.
342
 * @param {!Object} theType The type definition.
343
 * @private
344
 */
345
function setWriter_(theType) {
346
    if (theType["float"]) {
347
        if (theType["bits"] == 16) {
348
            writer_ = write16F_;
349
        } else if(theType["bits"] == 32) {
350
            writer_ = write32F_;
351
        } else if(theType["bits"] == 64) {
352
            writer_ = write64F_;
353
        }
354
    } else if (theType["char"]) {
355
        writer_ = writeChar_;
356
    } else {
357
        writer_ = writeInt_;
358
    }   
359
}
360
361
/**
362
 * Validate the type and set up the packing/unpacking functions.
363
 * @param {!Object} theType The type definition.
364
 * @throws {Error} If the type definition is not valid.
365
 * @private
366
 */
367
function setUp_(theType) {
368
    validateType_(theType);
369
    theType["offset"] = theType["bits"] < 8 ? 1 : Math.ceil(theType["bits"] / 8);
370
    setReader_(theType);
371
    setWriter_(theType);
372
    if (!theType["char"]) {
373
        gInt_ = new GenericInteger(
374
            theType["bits"] == 64 ? 32 : theType["bits"],
375
            theType["float"] ? false : theType["signed"]);    
376
    } else {
377
        // Workaround; should not use GenericInteger when type["char"]
378
        gInt_.offset = theType["bits"] < 8 ? 1 : Math.ceil(theType["bits"] / 8);
379
    }
380
}
381
382
/**
383
 * Validate the type definition.
384
 * @param {!Object} theType The type definition.
385
 * @throws {Error} If the type definition is not valid.
386
 * @private
387
 */
388
function validateType_(theType) {
389
    if (!theType) {
390
        throw new Error("Undefined type.");
391
    }
392
    if (theType["float"]) {
393
        if ([16,32,64].indexOf(theType["bits"]) == -1) {
394
            throw new Error("Not a supported float type.");
395
        }
396
    } else {
397
        if (theType["char"]) {
398
            if (theType["bits"] < 8 || theType["bits"] % 2) {
399
                throw new Error("Wrong offset for type char.");  
400
            }
401
        } else {
402
            if (theType["bits"] < 1 || theType["bits"] > 53) {
403
                throw new Error("Not a supported type.");  
404
            }
405
        }
406
    }
407
}
408
409
/**
410
 * Fix strings with bad length by either truncating when length is
411
 * greater than the defined in the type or turning them to "" if
412
 * length is smaller than defined in the type. If the value is not a
413
 * string, just return the value.
414
 * @param {!string|number} value The string to fix.
415
 * @param {!Object} theType The type definition.
416
 * @return {!string|number}
417
 * @private
418
 */
419
function fixBadString_(value, theType) {
420
    if (value.constructor == String) {
421
        if (value.length >= theType["offset"]) {
422
            value = value.slice(0, theType["offset"]);
423
        } else {
424
            value = "";
425
        }
426
    }
427
    return value;
428
}
429
430
// Types
431
/**
432
 * A char.
433
 * @type {!Object}
434
 */
435
module.exports.chr = {"bits": 8, "char": true};
436
/**
437
 * A 4-char string
438
 * @type {!Object}
439
 */
440
module.exports.fourCC = {"bits": 32, "char": true};
441
/**
442
 * Booleans
443
 * @type {!Object}
444
 */
445
module.exports.bool = {"bits": 1};
446
/**
447
 * Signed 2-bit integers
448
 * @type {!Object}
449
 */
450
module.exports.int2 = {"bits": 2, "signed": true};
451
/**
452
 * Unsigned 2-bit integers
453
 * @type {!Object}
454
 */
455
module.exports.uInt2 = {"bits": 2};
456
/**
457
 * Signed 4-bit integers
458
 * @type {!Object}
459
 */
460
module.exports.int4 = {"bits": 4, "signed": true};
461
/**
462
 * Unsigned 4-bit integers
463
 * @type {!Object}
464
 */
465
module.exports.uInt4 = {"bits": 4};
466
/**
467
 * Signed 8-bit integers
468
 * @type {!Object}
469
 */
470
module.exports.int8 = {"bits": 8, "signed": true};
471
/**
472
 * Unsigned 4-bit integers
473
 * @type {!Object}
474
 */
475
module.exports.uInt8 = {"bits": 8};
476
// LE
477
/**
478
 * Signed 16-bit integers little-endian
479
 * @type {!Object}
480
 */
481
module.exports.int16  = {"bits": 16, "signed": true};
482
/**
483
 * Unsigned 16-bit integers little-endian
484
 * @type {!Object}
485
 */
486
module.exports.uInt16 = {"bits": 16};
487
/**
488
 * Half-precision floating-point numbers little-endian
489
 * @type {!Object}
490
 */
491
module.exports.float16 = {"bits": 16, "float": true};
492
/**
493
 * Signed 24-bit integers little-endian
494
 * @type {!Object}
495
 */
496
module.exports.int24 = {"bits": 24, "signed": true};
497
/**
498
 * Unsigned 24-bit integers little-endian
499
 * @type {!Object}
500
 */
501
module.exports.uInt24 = {"bits": 24};
502
/**
503
 * Signed 32-bit integers little-endian
504
 * @type {!Object}
505
 */
506
module.exports.int32 = {"bits": 32, "signed": true};
507
/**
508
 * Unsigned 32-bit integers little-endian
509
 * @type {!Object}
510
 */
511
module.exports.uInt32 = {"bits": 32};
512
/**
513
 * Single-precision floating-point numbers little-endian
514
 * @type {!Object}
515
 */
516
module.exports.float32 = {"bits": 32, "float": true};
517
/**
518
 * Signed 40-bit integers little-endian
519
 * @type {!Object}
520
 */
521
module.exports.int40 = {"bits": 40, "signed": true};
522
/**
523
 * Unsigned 40-bit integers little-endian
524
 * @type {!Object}
525
 */
526
module.exports.uInt40 = {"bits": 40};
527
/**
528
 * Signed 48-bit integers little-endian
529
 * @type {!Object}
530
 */
531
module.exports.int48 = {"bits": 48, "signed": true};
532
/**
533
 * Unsigned 48-bit integers little-endian
534
 * @type {!Object}
535
 */
536
module.exports.uInt48 = {"bits": 48};
537
/**
538
 * Double-precision floating-point numbers little-endian
539
 * @type {!Object}
540
 */
541
module.exports.float64 = {"bits": 64, "float": true};
542
// BE
543
/**
544
 * Signed 16-bit integers big-endian
545
 * @type {!Object}
546
 */
547
module.exports.int16BE  = {"bits": 16, "signed": true, "be": true};
548
/**
549
 * Unsigned 16-bit integers big-endian
550
 * @type {!Object}
551
 */
552
module.exports.uInt16BE = {"bits": 16, "be": true};
553
/**
554
 * Half-precision floating-point numbers big-endian
555
 * @type {!Object}
556
 */
557
module.exports.float16BE = {"bits": 16, "float": true, "be": true};
558
/**
559
 * Signed 24-bit integers big-endian
560
 * @type {!Object}
561
 */
562
module.exports.int24BE = {"bits": 24, "signed": true, "be": true};
563
/**
564
 * Unsigned 24-bit integers big-endian
565
 * @type {!Object}
566
 */
567
module.exports.uInt24BE = {"bits": 24, "be": true};
568
/**
569
 * Signed 32-bit integers big-endian
570
 * @type {!Object}
571
 */
572
module.exports.int32BE = {"bits": 32, "signed": true, "be": true};
573
/**
574
 * Unsigned 32-bit integers big-endian
575
 * @type {!Object}
576
 */
577
module.exports.uInt32BE = {"bits": 32, "be": true};
578
/**
579
 * Single-precision floating-point numbers big-endian
580
 * @type {!Object}
581
 */
582
module.exports.float32BE = {"bits": 32, "float": true, "be": true};
583
/**
584
 * Signed 40-bit integers big-endian
585
 * @type {!Object}
586
 */
587
module.exports.int40BE = {"bits": 40, "signed": true, "be": true};
588
/**
589
 * Unsigned 40-bit integers big-endian
590
 * @type {!Object}
591
 */
592
module.exports.uInt40BE = {"bits": 40, "be": true};
593
/**
594
 * Signed 48-bit integers big-endian
595
 * @type {!Object}
596
 */
597
module.exports.int48BE = {"bits": 48, "signed": true, "be": true};
598
/**
599
 * Unsigned 48-bit integers big-endian
600
 * @type {!Object}
601
 */
602
module.exports.uInt48BE = {"bits": 48, "be": true};
603
/**
604
 * Double-precision floating-point numbers big-endian
605
 * @type {!Object}
606
 */
607
module.exports.float64BE = {"bits": 64, "float": true, "be": true};
608
609
// Methods
610
module.exports.pack = pack;
611
module.exports.unpack = unpack;
612
module.exports.packArray = packArray;
613
module.exports.unpackArray = unpackArray;
614